package cn.myzf.netty.client.handler;


import com.hs.netty.common.protobuf.Message;
import com.hs.netty.common.util.ReflectionUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder;
import io.netty.handler.codec.protobuf.ProtobufVarint32LengthFieldPrepender;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * netty客户端
 *
 * @author lenovo
 */
@Component
@Slf4j
public class NettyClient {
    private final String HOST = ReflectionUtil.getValue("spring.netty.host");
    private final int PORT = Integer.valueOf(ReflectionUtil.getValue("spring.netty.port"));
    private final static int READER_IDLE_TIME_SECONDS = 0;//读操作空闲20秒
    private final static int WRITER_IDLE_TIME_SECONDS = 60;//写操作空闲20秒
    private final static int ALL_IDLE_TIME_SECONDS = 0;//读写全部空闲40秒

    private EventLoopGroup loop = new NioEventLoopGroup();


    public void run() throws Exception {
        doConnect(new Bootstrap(), loop);
    }

    /**
     * netty client 连接，连接失败5秒后重试连接
     */
    public Bootstrap doConnect(Bootstrap bootstrap, EventLoopGroup eventLoopGroup) {
        try {
            if (bootstrap != null) {
                bootstrap.group(eventLoopGroup);
                bootstrap.channel(NioSocketChannel.class);
                bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
                bootstrap.handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline p = ch.pipeline();

                        p.addLast("idleStateHandler", new IdleStateHandler(READER_IDLE_TIME_SECONDS
                                , WRITER_IDLE_TIME_SECONDS, ALL_IDLE_TIME_SECONDS, TimeUnit.SECONDS));

                        p.addLast(new ProtobufVarint32FrameDecoder());
                        p.addLast(new ProtobufDecoder(Message.MessageBase.getDefaultInstance()));

                        p.addLast(new ProtobufVarint32LengthFieldPrepender());
                        p.addLast(new ProtobufEncoder());

                        p.addLast("clientHandler", new AuthClientHandler());
                        p.addLast("idleTimeoutHandler", new HeartHandler(NettyClient.this));

                    }
                });
                bootstrap.remoteAddress(HOST, PORT);
                ChannelFuture f = bootstrap.connect().addListener((ChannelFuture futureListener) -> {
                    final EventLoop eventLoop = futureListener.channel().eventLoop();
                    if (!futureListener.isSuccess()) {
                        log.warn("连接服务器失败，5s后重新尝试连接！");
                        futureListener.channel().eventLoop().schedule(() -> doConnect(new Bootstrap(), eventLoop), 5, TimeUnit.SECONDS);
                    }
                });
                f.channel().closeFuture().sync();
                //eventLoopGroup.shutdownGracefully();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return bootstrap;
    }

}
